package modele;


import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

import outils.Divers;

public class ModeleClasse extends ModeleXml 
{

	//nom de la classe
	private String nom;
	private Element eNom;
	private Element eAttributs;
	private Element eMethodes;
	private Element ePositions;
	private Element eX;
	private Element eY; //position x et y de la classe 
	//passe à vrai dès qu'il y a au moins un attribut
	private boolean at;
	
	
	public boolean isAt() {
		return at;
	}


	public void setAt(boolean at) {
		this.at = at;
	}


	public boolean isRel() {
		return rel;
	}


	public void setRel(boolean rel) {
		this.rel = rel;
	}


	public boolean isMeth() {
		return meth;
	}


	public void setMeth(boolean meth) {
		this.meth = meth;
	}

	//passe à vrai dès qu'il y a au moins une relation
	private boolean rel;
	//passe à vrai dès qu'il y a au moins une méthode
	private boolean meth;
	/**
	 * Construit le squelette
	 */
	public ModeleClasse()
	{
		this(" ");
	}

	public ModeleClasse(ModeleClasse m) {
		this(m.getNom());
		
		// On copie chaque attribut de la classe
		Vector<ModeleAttribut> attributs = m.getAttributs();
		for(ModeleAttribut ma : attributs) {
			addAttribut(new ModeleAttribut(ma.getVisib(), ma.getNom(), ma.getType()));
		}
		
		// On copie chaque méthode de la classe
		Vector<ModeleMethode> methodes = m.getMethodes();
		for(ModeleMethode mm : methodes) {
			addMethode(new ModeleMethode(mm.getNom(), mm.getTypeRetour(),"void", mm.getListeArgs()));
		}
	}
	
	public ModeleClasse(String n)
	{
		super.root = new Element("classe");
		super.doc = new Document(root);
		this.nom = n;
		
		eNom = new Element("nom");
		eNom.setText(this.nom);
		root.addContent(eNom);
		
		this.eAttributs = new Element("attributs");
		root.addContent(this.eAttributs);
		
		this.eMethodes = new Element("methodes");
		root.addContent(this.eMethodes);
			
		this.ePositions = new Element("positions");
		root.addContent(this.ePositions);
		
		this.eX = new Element("x");
		this.ePositions.addContent(this.eX);
		this.eY = new Element("y");
		this.ePositions.addContent(this.eY);
		
		this.at = false;
		this.rel = false;
		this.meth = false;
	}
	
	public ModeleClasse(Element classeXml)
	{
		 //on crée l'instance avec le nom de la classe dans le xml
		 this(classeXml.getChild("nom").getText());
		 //les sous-racines definissant la classe
		 Element racineAttribut = classeXml.getChild("attributs");
		 Element racineMethode = classeXml.getChild("methodes");
		 Element racinePosition = classeXml.getChild("positions");
		 Element racineArgument = classeXml.getChild("arguments");
		 Element curseur;
		 Element curseurArgs;
		 String vis ="", nom = "",type ="", nomMeth ="", typeRet="", visMeth="";
		 Vector<String> lesArgus;

		 
		 //on récupère la liste des attributs, des methodes, des relations, et des positions 
		 List<Element> listAttribut = racineAttribut.getChildren();
		 Iterator<Element> itAtt = listAttribut.iterator();
		 
		 List<Element> listMethode = racineMethode.getChildren();
		 Iterator<Element> itMeth = listMethode.iterator();
		 
		 List<Element> listArgument; 
		 Iterator<Element>itArgus;
		 //on recupere chaque attribut et on l'ajoute au modelClasse
		 while(itAtt.hasNext())
		 {
			 curseur = itAtt.next();
			 System.out.println("nom de lattribut "+curseur.getName());
			  vis = curseur.getChildText("visibilite");
			 nom = curseur.getChildText("nomAtt");
			 type = curseur.getChildText("type");
			 System.out.println(" visibilite: "+vis+" nom: "+nom+" type: "+type);
			 //on ajoute l'attribut récupéré
			 this.addAttribut(new ModeleAttribut(Divers.stringToVisib(vis), nom, type));
		 }
		 
		 //on recupere chaque meth et on l'ajoute au modelClasse
		 while(itMeth.hasNext())
		 {
			 lesArgus = new Vector<String>();
			 curseur = itMeth.next();
			 visMeth = curseur.getChildText("visibilite");
			 nomMeth = curseur.getChildText("nomMeth");
			 typeRet = curseur.getChildText("typeRetour");
			 listArgument = curseur.getChild("arguments").getChildren();
			 itArgus = listArgument.iterator();
			 while(itArgus.hasNext())
			 {
				 curseurArgs = itArgus.next();
				 lesArgus.add(curseurArgs.getText());
			 }
			 this.addMethode(new ModeleMethode(visMeth,nomMeth, typeRet, lesArgus));
		 }
			 
			 //et enfin on recupere les positions
			String xx = racinePosition.getChildText("x");
			System.out.println("xx "+xx);
			String yy = racinePosition.getChildText("y");
			System.out.println("yy "+yy);
			int x = Integer.parseInt(xx);
			int y = Integer.parseInt(yy);
			this.setPosition(x, y);
			//tempClasse.affiche();
}
	
	public void setNom(String nom)
	{
		this.nom = nom;
		this.eNom.setText(this.nom);
	}
	
	
	public String getNom() 
	{
		return nom;
	}
	
	
	
	public Vector<ModeleAttribut> getAttributs()
	{
		Vector<ModeleAttribut> atList = new Vector<ModeleAttribut>();
		//on verifie s'il y au moins un attribut
		if(this.at)
		{
			List<Element> la = allerAuNoeud("attributs", "attribut");
			Iterator<Element> i = la.iterator();
			String nom = "";
			String visib = "";
			String type = "";
			while(i.hasNext())
			{
				Element current = (Element) i.next();
				visib = current.getChildText("visibilite");
				nom = current.getChildText("nomAtt");
				type = current.getChildText("type");	
				atList.add(new ModeleAttribut(Divers.stringToVisib(visib), nom, type));
			}		
		}
		return atList;
	}
	
	

	public Vector<ModeleMethode> getMethodes()
	{
		Vector<ModeleMethode> listeMeth =  new Vector<ModeleMethode>();
		if(this.meth)
		{
			List<Element> la = allerAuNoeud("methodes", "methode");
			Iterator<Element> i = la.iterator();
			Vector<String> typeArgs;
			String nom = "", visib="";
			String typeRetour = "" ;
			while(i.hasNext())
			{
				typeArgs = new Vector<String>();
				Element current = (Element) i.next();
				visib = current.getChildText("visibilite");
				nom = current.getChildText("nomMeth");
				typeRetour = current.getChildText("typeRetour");
				
				Element racineArg = current.getChild("arguments");
				 
				List<Element> listArg = racineArg.getChildren("argument");
				Iterator<Element> j = listArg.iterator();
				while(j.hasNext())
				{
					Element currentArg = (Element) j.next();
					typeArgs.add(currentArg.getText());
				}
				listeMeth.add(new ModeleMethode(visib,nom,typeRetour,typeArgs));
			}		
		}
		
		return listeMeth;
	}
	

	
	public void setPosition(int x, int y)
	{	
		this.eX.setText(String.valueOf(x));
		this.eY.setText(String.valueOf(y));
	}
	
	public int getPositionX()
	{
		int x = Integer.parseInt(this.eX.getText());
		return x;
	}
	

	public int getPositionY()
	{
		int y = Integer.parseInt(this.eY.getText());
		return y;
	}
	
	
	//ajoute un attribut à la classe à ne pas confondre avec les attributs en xml!
	public void addAttribut(ModeleAttribut a)
	{
		if(!this.at)
			this.at = true;
		//on crée les éléments nécessaires
		Element eAttRoot = new Element("attribut");
		Element eVisibilite = new Element("visibilite");
		Element eNomAtt = new Element("nomAtt"); 
		Element eType = new Element("type");
		
		this.eAttributs.addContent(eAttRoot);
		eAttRoot.addContent(eVisibilite);
		eVisibilite.setText(a.getVisib().toString());
		
		eAttRoot.addContent(eNomAtt);
		eNomAtt.setText(a.getNom());
		
		eAttRoot.addContent(eType);
		eType.setText(a.getType().toString());		
	}
	
	
	public void addMethode(ModeleMethode m)
	{
		if(!this.meth)
			this.meth = true;
		Element eMethRoot = new Element("methode");
		Element eMethVis = new Element("visibilite");
		Element eMethName = new Element("nomMeth");
		Element eTypeRet = new Element("typeRetour");
		Element eArgs = new Element("arguments");
		
		this.eMethodes.addContent(eMethRoot);
		eMethRoot.addContent(eMethVis);
		eMethVis.setText(m.getVisib());
		eMethRoot.addContent(eMethName);
		eMethName.setText(m.getNom());
		
		eMethRoot.addContent(eTypeRet);
		eTypeRet.setText(m.getTypeRetour());
		
		eMethRoot.addContent(eArgs);

		if(!m.getListeArgs().isEmpty())
		{
			Element eArgTemp;
			for(int i=0;i<m.getListeArgs().size();i++)
			{
				eArgTemp = new Element("argument");
				eArgs.addContent(eArgTemp);
				System.out.println(m.getListeArgs().get(i));
				eArgTemp.setText(m.getListeArgs().get(i));
				
			}
		}
	
	}
	
	
	//ajoute une relation à la classe
	public void addRelation(ModeleRelation r, boolean rec)
	{
		if(!this.rel)
			this.rel = true;
		Element eRelRoot = new Element("relation"); 
		Element eType = new Element("typeRelation");
		Element eClasse = new Element("avec");
		Element eCard = new Element("cardinalite");
	
		eRelRoot.addContent(eType);
		eType.setText(r.getTypeRelation().toString());
		System.out.println("texte du type "+ eType.getText());
		eRelRoot.addContent(eClasse);
		eClasse.setText(r.getClasseDest().getNom());
		eRelRoot.addContent(eCard);
		eCard.setText(r.getCardDest().toString());
		
		//pour representer la relation dans l'autre classe
		if(rec)
		r.getClasseDest().addRelation(new ModeleRelation
				(r.getTypeRelation(),
				r.getClasseDest(),r.getClasseSource(),
				r.getCardDest(),r.getCardSrc()),false);
	}
	
	
	public boolean delAttribut(ModeleAttribut a)
	{	
		boolean check = false;
		//on récupère la liste des attributs
		List<Element> lAtt = allerAuNoeud("attributs", "attribut");
		Element temp;		
		for(int i =0;i<lAtt.size();i++)
		{
			temp = (Element)lAtt.get(i);
			//on compare la visibilté, le nom et le type
			if(temp.getChildText("visibilite").equals(a.getVisib().toString())
				&& temp.getChildText("nomAtt").equals(a.getNom())
				&& temp.getChildText("type").equals(a.getType())
			  )
			{
					temp.removeContent();
					lAtt.remove(i);
					check = true;
					break;
			}
		}
		return check;
	}
	
	public boolean delRelation(ModeleRelation r)
	{	
		boolean check = false; 
		List<Element> lAtt = allerAuNoeud("relations", "relation");
		Element temp;		
		for(int i = 0;i<lAtt.size();i++)
		{
			temp = (Element)lAtt.get(i); 
			System.out.println(temp.getChildText("typeRelation")+"____"+r.getTypeRelation());
			System.out.println(temp.getChildText("avec")+"____"+r.getClasseDest().getNom());
			System.out.println(temp.getChildText("cardinalite")+"____"+r.getCardDest());
			
			if( temp.getChildText("typeRelation").equals(r.getTypeRelation().toString())
				&& temp.getChildText("avec").equals(r.getClasseDest().getNom())
				&& temp.getChildText("cardinalite").equals(r.getCardDest())
			  )
			{
					temp.removeContent();
					lAtt.remove(i);
					check = true;
					break;
			}
		}
		return check;
	}
	
	//on modifie un attribut en spécifiant un nouvel objet Attribut
	public boolean modAttribut(ModeleAttribut ancien, ModeleAttribut nouveau)
	{
		boolean check = false;
		List<Element> lAtt = allerAuNoeud("attributs", "attribut");
		Element temp;
		for(int i =0; i<lAtt.size(); i++)
		{
			temp = (Element)lAtt.get(i);
			//on compare la visibilité et le nom 
			if(temp.getChildText("visibilite").toString().equals(ancien.getVisib().toString())
				&& temp.getChildText("nomAtt").equals(ancien.getNom())
				&& temp.getChildText("type").equals(ancien.getType())
			  )
			{
					//on met à jour la valeur de chaque noeud
					temp.getChild("visibilite").setText(nouveau.getVisib().toString());
					temp.getChild("nomAtt").setText(nouveau.getNom());
					temp.getChild("type").setText(nouveau.getType());
					check = true;
					break;
			}
		}
		return check;
	}
		
	//permet d'aller à un noeud donné, en précisant la racine
	//ex: allerAuNoeud("attributs","attribut")
	private List<Element> allerAuNoeud(String racine, String noeud)
	{
		List<Element> l = this.root.getChildren(racine);
		List<Element> lAtt = l.get(0).getChildren(noeud);
		return lAtt;
	}		
}